using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace bkfviewer {
    public partial class HexView : UserControl {
        public HexView() {
            InitializeComponent();
        }

        private void HexView_Load(object sender, EventArgs e) {
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.Selectable, true);
            SetStyle(ControlStyles.ResizeRedraw, true);
            this.DoubleBuffered = true;
        }

        [NonSerialized()]
        Stream si = null;

        [NonSerialized()]
        Int64 pos = 0;

        public Stream Contents { get { return si; } set { si = value; Invalidate(); } }

        public Int64 Position { get { return pos; } set { pos = value; Invalidate(); } }

        private void HexView_Paint(object sender, PaintEventArgs e) {
            Graphics cv = e.Graphics;
            if (DesignMode) {
                ControlPaint.DrawBorder(cv, Rectangle.Inflate(ClientRectangle, -0, -0), Color.Gray, ButtonBorderStyle.Dashed);
                return;
            }
            if (si == null) return;
            SizeF size = cv.MeasureString("0123456789abc", this.Font);
            float ccx = size.Width / 13;
            float ccy = size.Height;
            int yc = (int)((ClientSize.Height - 2) / ccy);
            byte[] bin = new byte[16];
            si.Position = pos;
            StringBuilder s = new StringBuilder();
            SolidBrush br = new SolidBrush(this.ForeColor);
            PointF pt = new PointF(1, 1);
            for (int y = 0; y < yc; y++, pt.Y += ccy) {
                s.Length = 0;
                s.AppendFormat("{0,10:x8}:", si.Position);
                int r = si.Read(bin, 0, 16);
                int x;
                for (x = 0; x < r; x++) {
                    s.AppendFormat(" {0:x2}", bin[x]);
                }
                for (; x < 16; x++) s.Append("   ");

                s.Append(" ");

                for (x = 0; x < r; x++) {
                    char c = (char)bin[x];
                    s.Append((char.IsLetterOrDigit(c) || char.IsPunctuation(c) || char.IsSeparator(c)) ? c : '.');
                }
                cv.DrawString(s.ToString(), this.Font, br, pt);
                if (x != 16) break;
            }
        }

        public int DisplayedLines {
            get {
                using (Graphics cv = CreateGraphics()) {
                    SizeF size = cv.MeasureString("0123456789abc", this.Font);
                    return (int)((ClientSize.Height - 2) / size.Height);
                }
            }
        }

        public Int64 MaxPosition { get { return (si != null) ? si.Length : 0; } }

        private void HexView_KeyDown(object sender, KeyEventArgs e) {
            switch (e.KeyCode) {
                case Keys.Home:
                    this.Position = 0;
                    e.Handled = true;
                    break;
                case Keys.End:
                    this.Position = MaxPosition;
                    e.Handled = true;
                    break;
                case Keys.Left:
                    this.Position = Math.Max(0, this.Position - 1);
                    e.Handled = true;
                    break;
                case Keys.Right:
                    this.Position = Math.Min(MaxPosition, this.Position + 1);
                    e.Handled = true;
                    break;
                case Keys.Up:
                    this.Position = Math.Max(0, this.Position - 16);
                    e.Handled = true;
                    break;
                case Keys.Down:
                    this.Position = Math.Min(MaxPosition, this.Position + 16);
                    e.Handled = true;
                    break;
                case Keys.PageUp:
                    this.Position = Math.Max(0, this.Position - DisplayedLines * 16);
                    e.Handled = true;
                    break;
                case Keys.PageDown:
                    this.Position = Math.Min(MaxPosition, this.Position + DisplayedLines * 16);
                    e.Handled = true;
                    break;
            }
        }

        private void HexView_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e) {
            switch (e.KeyCode) {
                case Keys.Left:
                case Keys.Up:
                case Keys.Right:
                case Keys.Down:
                    e.IsInputKey = true;
                    break;
            }
        }
    }
}
